#include <ptgui/ptgui.h>
#include <ptgui/event.h>
#include <ptgui/widgets/container.h>

static void _ptgui_container_constructor(ptgui_container_t *container)
{
	/* set event handler and init field */
	ptgui_widget_set_event_handler(PTGUI_WIDGET(container), ptgui_container_event_handler);
	ptgui_list_init(&(container->children));

	/* set focused widget to itself */
	container->focused = PTGUI_WIDGET(container);
	/* set container as focusable widget */
	PTGUI_WIDGET(container)->flag |= PTGUI_WIDGET_FLAG_FOCUSABLE;
}

static void _ptgui_container_destructor(ptgui_container_t *container)
{
	/* destroy children of container */
	ptgui_container_destroy_children(container);
}

ptgui_type_t * ptgui_container_type_get(void)
{
	static ptgui_type_t *container_type = RT_NULL;

	if (!container_type) {
		container_type = ptgui_type_create("container", PTGUI_WIDGET_TYPE, sizeof(ptgui_container_t),
										   PTGUI_CONSTRUCTOR(_ptgui_container_constructor),
										   PTGUI_DESTRUCTOR(_ptgui_container_destructor));
	}

	return container_type;
}

rt_bool_t ptgui_container_dispatch_event(ptgui_container_t *container, ptgui_event_t *event)
{
	/* handle in child widget */
	struct ptgui_list_node *node;

	ptgui_list_foreach(node, &(container->children))
	{
		struct ptgui_widget *w;
		w = ptgui_list_entry(node, struct ptgui_widget, sibling);

		if (w->event_handler(w, event) == RT_TRUE) {
			return RT_TRUE;
		}
	}

	return RT_FALSE;
}

rt_bool_t ptgui_container_dispatch_mouse_event(ptgui_container_t *container, struct ptgui_event_mouse *event)
{
	/* handle in child widget */
	struct ptgui_list_node *node;

	ptgui_list_foreach(node, &(container->children))
	{
		struct ptgui_widget *w;
		w = ptgui_list_entry(node, struct ptgui_widget, sibling);
		if (ptgui_rect_contains_point(&(w->extent), event->x, event->y) == RT_EOK) {
			if (w->event_handler(w, (ptgui_event_t *)event) == RT_TRUE) {
				return RT_TRUE;
			}
		}
	}

	return RT_FALSE;
}

rt_bool_t ptgui_container_event_handler(ptgui_widget_t *widget, ptgui_event_t *event)
{
	ptgui_container_t *container = PTGUI_CONTAINER(widget);

	switch (event->type) {
	case PTGUI_EVENT_PAINT:
		ptgui_container_dispatch_event(container, event);
		break;

	case PTGUI_EVENT_KBD:
		{
			/* let parent to handle keyboard event */
			if (widget->parent != RT_NULL && widget->parent != NULL) {
				return widget->parent->event_handler(widget->parent, event);
			}
		}
		break;

	case PTGUI_EVENT_MOUSE_BUTTON:
		/* handle in child widget */
		if (ptgui_container_dispatch_mouse_event(container, (struct ptgui_event_mouse *)event) == RT_FALSE) {
		}
		else {
			return RT_TRUE;
		}
		break;

	case PTGUI_EVENT_MOUSE_MOTION:
		if (ptgui_container_dispatch_mouse_event(container, (struct ptgui_event_mouse *)event) == RT_FALSE) {
		}
		else {
			return RT_TRUE;
		}
		break;

	case PTGUI_EVENT_COMMAND:
		if (ptgui_container_dispatch_event(container, event) == RT_FALSE) {
		}
		else {
			return RT_TRUE;
		}
		break;

	default:
		/* call parent widget event handler */
		return ptgui_widget_event_handler(widget, event);
	}

	return RT_FALSE;
}

/*
 * This function will add a child to a container widget
 * Note: this function will not change the widget layout
 * the layout is the responsibility of layout widget, such as box.
 */
void ptgui_container_add_child(ptgui_container_t *container, ptgui_widget_t *child)
{
	RT_ASSERT(container != RT_NULL);
	RT_ASSERT(child != RT_NULL);

	/* set parent and toplevel widget */
	child->parent = PTGUI_WIDGET(container);
	/* put widget to parent's children list */
	ptgui_list_append(&(container->children), &(child->sibling));
}

/* remove a child to widget */
void ptgui_container_remove_child(ptgui_container_t *container, ptgui_widget_t *child)
{
	RT_ASSERT(container != RT_NULL);
	RT_ASSERT(child != RT_NULL);

	if (child == container->focused) {
		/* set focused to itself */
		container->focused = PTGUI_WIDGET(container);

		ptgui_widget_focus(PTGUI_WIDGET(container));
	}

	/* remove widget from parent's children list */
	ptgui_list_remove(&(container->children), &(child->sibling));

	/* set parent and toplevel widget */
	child->parent = RT_NULL;
}

/* destroy all children of container */
void ptgui_container_destroy_children(ptgui_container_t *container)
{
	struct ptgui_list_node *node;

	if (container == RT_NULL) {
		return;
	}

	node = container->children.next;
	while (node != RT_NULL) {
		ptgui_widget_t *child = ptgui_list_entry(node, ptgui_widget_t, sibling);

		if (PTGUI_IS_CONTAINER(child)) {
			/* break parent firstly */
			child->parent = RT_NULL;

			/* destroy children of child */
			ptgui_container_destroy_children(PTGUI_CONTAINER(child));
		}

		/* remove widget from parent's children list */
		ptgui_list_remove(&(container->children), &(child->sibling));

		/* set parent and toplevel widget */
		child->parent = RT_NULL;

		/* destroy object and remove from parent */
		ptgui_object_destroy(PTGUI_OBJECT(child));

		node = container->children.next;
	}

	container->children.next = RT_NULL;
	container->focused = PTGUI_WIDGET(container);
	if (PTGUI_WIDGET(container)->parent != RT_NULL) {
		ptgui_widget_focus(PTGUI_WIDGET(container));
	}
}

ptgui_widget_t * ptgui_container_get_first_child(ptgui_container_t *container)
{
	ptgui_widget_t *child = RT_NULL;

	if (container->children.next != RT_NULL) {
		child = ptgui_list_entry(container->children.next, ptgui_widget_t, sibling);
	}

	return child;
}
